using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for Net.
	/// </summary>
	public class Net
	{
		System.Collections.ArrayList oSourceNodes;
		System.Collections.ArrayList oSinkNodes;
		System.Collections.ArrayList oPassThroughNodes;

		SimulationState eSimulationStateOutput;
		NodeState eNetState;

		int iPropagationDelay;

		EventNode oEventNodeStabilize;
		RapidHardware oRapidHardware;

		// destabilize
		// stabilize

		public Net(Node poNode)
		{
			oEventNodeStabilize = null;
			iPropagationDelay = 0;
			eNetState = NodeState.Undefined;
			oSourceNodes = new System.Collections.ArrayList();
			oSinkNodes = new System.Collections.ArrayList();
			oPassThroughNodes = new System.Collections.ArrayList();
			eSimulationStateOutput = SimulationState.Stable;
			oRapidHardware = poNode.ParentNodeVector.ParentComponent.RapidHardware;
			this.Connect(poNode);
		}

		public void Connect(Net poNet)
		{
			if (this.NodeCount < poNet.NodeCount)
			{
				poNet.Connect(this);
				return;
			}
			foreach(Node oNode in poNet.SourceNodes)
			{
				this.Connect(oNode);
			}
			foreach(Node oNode in poNet.SinkNodes)
			{
				this.Connect(oNode);
			}
			foreach(Node oNode in poNet.PassthroughNodes)
			{
				this.Connect(oNode);
			}
		}

		public void Connect(Node poNode)
		{
			switch (poNode.NodeFlowType)
			{
				case NodeFlowType.Source:
					oSourceNodes.Add(poNode);
					break;
				case NodeFlowType.Sink:
					oSinkNodes.Add(poNode);
					break;
				case NodeFlowType.Passthrough:
					oPassThroughNodes.Add(poNode);
					break;
			}				
			poNode.Net = this;
		}

		public int NodeCount
		{
			get
			{
				return oPassThroughNodes.Count + oSinkNodes.Count + oSourceNodes.Count;
			}
		}

		public SimulationState SimulationStateInput
		{
			get
			{
				foreach(Node oNode in oSourceNodes)
				{
					if (oNode.ParentNodeVector.ParentComponent.SimulationStateOutput == SimulationState.Unstable)
					{
						SimulationTraceLog("This net's input node is unstable.",oNode);
						return SimulationState.Unstable;
					}
				}
				SimulationTraceLog("This net's inputs are stable.",null);
				return SimulationState.Stable;
			}
		}

		public NodeState NodeState
		{
			get
			{
				if (oSourceNodes.Count == 0)
					return eNetState;

				NodeState eNodeState = NodeState.Tri;
				foreach(Node oNode in oSourceNodes)
				{
					if (oNode.NodeState != NodeState.Tri)
					{
						if (eNodeState == NodeState.Tri)
							eNodeState = oNode.NodeState;
						else
						{
							if (eNodeState != oNode.NodeState)
								return NodeState.Undefined;
						}
					}
				}
				return eNodeState;
			}

			set
			{
				if (oSourceNodes.Count == 0)
				{
					if (eNetState != value)
					{
						eNetState = value;
						SimulationDestabilizeNet();
						SimulationStabilizeNet();
					}
				}

			}
		}

		public Node SelectCriticalNode(Component poComponent)
		{
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NodeStructure.Connection | oNode.NodeStructure == NodeStructure.Input | oNode.NodeStructure == NodeStructure.Output)
						return oNode;
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NodeStructure.Connection | oNode.NodeStructure == NodeStructure.Input | oNode.NodeStructure == NodeStructure.Output)
						return oNode;
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NodeStructure.Connection | oNode.NodeStructure == NodeStructure.Input | oNode.NodeStructure == NodeStructure.Output)
						return oNode;
			}

			return null;
		}

		public System.Collections.ArrayList SelectNodes(Component poComponent, NodeStructure peNodeStructure)
		{
			System.Collections.ArrayList oResult = new System.Collections.ArrayList();
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNodeStructure)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNodeStructure)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNodeStructure)
					oResult.Add(oNode);
			}

			return oResult;
		}


		public System.Collections.ArrayList SelectNodes(Component poComponent)
		{
			System.Collections.ArrayList oResult = new System.Collections.ArrayList();
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			return oResult;
		}

		public SimulationState SimulationStateOutput
		{
			get{return eSimulationStateOutput;}
		}

		public System.Collections.ArrayList PassthroughNodes
		{
			get {return oPassThroughNodes;}
		}

		public System.Collections.ArrayList SourceNodes
		{
			get {return oSourceNodes;}
		}

		public System.Collections.ArrayList SinkNodes
		{
			get {return oSinkNodes;}
		}


		public void SimulationDestabilizeNet()
		{
			// if net output is already unstable...
			//		we only need to cancel any scheduled stabilization event
			if (eSimulationStateOutput == SimulationState.Unstable)
			{
				SimulationTraceLog("net is already unstable",null); 

				// cancel stabilization event
				if (oEventNodeStabilize != null)
					oRapidHardware.Simulation.Clock.UnlinkNode(oEventNodeStabilize);
				oEventNodeStabilize = null;
				return;
			}

			// otherwise, we were a net stable, 
			// we need to alert sink nodes in the net that we are now unstable
			eSimulationStateOutput = SimulationState.Unstable;
			int i = 1;
			foreach(Node oNode in oSinkNodes)
			{
				SimulationTraceLog("Destabilizing Component {" + oNode.ParentNodeVector.ParentComponent.HirarchicalName + "} from Net via Node, loop " + i.ToString() + " of " + oSinkNodes.Count.ToString(),oNode); 
				oNode.ParentNodeVector.ParentComponent.SimulationDestabilizeComponent();
			}
		}

		public void SimulationStabilizeNet()
		{
			// should never call this when node is already stable
			System.Diagnostics.Debug.Assert(eSimulationStateOutput != SimulationState.Stable);

			// check to see if the inputs are all stable
			if (this.SimulationStateInput != SimulationState.Stable)
			{
				return;  // unstable inputs mean unstable net
			}

			// if there is no propagation delay, immediately stabilize output too
			//     but, if there is a propagation delay, schedule stabilization event on clock
			if (iPropagationDelay == 0)
			{
				SimulationStabilizeNetOutputEvent(0);
			}
			else
			{
				if (oEventNodeStabilize != null)
					oRapidHardware.Simulation.Clock.UnlinkNode(oEventNodeStabilize);
				oEventNodeStabilize = oRapidHardware.Simulation.Clock.ScheduleClockEvent(iPropagationDelay,new Clock.ClockEvent(this.SimulationStabilizeNetOutputEvent)); 
			}
		}

		public void SimulationStabilizeNetOutputEvent(long plTimeNow)
		{

			SimulationTraceLog("SimulationSabilizeComponentOutputEvent at time " + plTimeNow.ToString() + "/" + oRapidHardware.Simulation.Clock.TimeNow.ToString() ,null);

			// if oEventNode is not null when calling directly, then there is a fundamental scheduling problem
			System.Diagnostics.Debug.Assert(oEventNodeStabilize == null & plTimeNow == 0 | plTimeNow != 0,"Phantom EventNodeStabilize event scheduled in Net object!");

			// should never call this event if node is already stable
			System.Diagnostics.Debug.Assert(eSimulationStateOutput != SimulationState.Stable,"Net stabilization event called when net is already stable!");

			eSimulationStateOutput = SimulationState.Stable;

			int i = 1;
			foreach(Node oNode in oSinkNodes)
			{
				SimulationTraceLog("Stabilizing Component {" + oNode.ParentNodeVector.ParentComponent.HirarchicalName + "} from Net via Node, loop " + i.ToString() + " of " + oSinkNodes.Count.ToString(),oNode); 
				oNode.ParentNodeVector.ParentComponent.SimulationStabilizeComponent();
				i++;
			}	
		}

		private void SimulationTraceLog(string psLogEntry, Node poNode)
		{
			string sLogEntry = "Net: ";
			sLogEntry += psLogEntry;
			if (poNode != null)
				sLogEntry += " {Node: " + poNode.ParentNodeVector.ParentComponent.HirarchicalName.ToString() + "." + poNode.Name + " = '" + poNode.NodeState.ToString() + "'}";
			oRapidHardware.InternalLog.Log(LogType.SimulationTrace,sLogEntry);
		}

	}
}
